import java.util.LinkedList ;
import java.io.BufferedReader ;
import java.io.InputStreamReader ;
import javax.swing.* ;
import java.awt.* ;
import java.awt.event.* ;

public class Experimenter extends JPanel implements ActionListener {
    private static final INeuron [ ] neuronTypes = { new MapNeuron( ) ,
    new HHNeuron( ) } ;
    private static final ISynapse [ ] synapseTypes = { new PureHebbianPlasticity( null , null , 0. ) ,
    new STDPSynapse( null , null , 0. ) } ;
    private static final INeuralNetExperiment [ ] experimentTypes = { new Experiment0( ) ,
    new Experiment1( ) ,
    new Experiment2( ) } ;
    private static final IAppliedCurrent [ ] currentTypes = { new ConstantCurrent( .08 ) ,
    new SinusoidalCurrent( .08 , 50. , 0 , 0 , 4000 ) } ;
    
    private INeuron neuron = null ;
    private ISynapse synapse = null ;
    private INeuralNetExperiment e = null ;
    private IAppliedCurrent IApp = null ;
    private static int count = 1 ;
    private JButton goButton ;
    private JPanel optionsPanel ;
    private ParameterWindow nParams , sParams , eParams , cParams ;
    private JPanel buttonPanel ;
    
    public Experimenter( ) {
        super( new BorderLayout( ) ) ;
        
        int i ;
        JRadioButton button ;
        
        
        ButtonGroup neurons = new ButtonGroup( ) ;
        ButtonGroup synapses = new ButtonGroup( ) ;
        ButtonGroup experiments = new ButtonGroup( ) ;
        ButtonGroup currents = new ButtonGroup( ) ;
        
        buttonPanel = new JPanel( new GridLayout( 0 , 1 ) ) ;
        
        buttonPanel.add( new JLabel( "Choose Neuron" ) ) ;
        for ( i = 0 ; i < neuronTypes.length ; i ++ ) {
            button = new JRadioButton( neuronTypes[i].getName( ) ) ;
            if ( i == 0 ) {
                button.setSelected( true ) ;
                neuron = neuronTypes [ i ] ;
            }
            button.setActionCommand( "n" + i ) ;
            button.addActionListener( this ) ;
            neurons.add( button ) ;
            buttonPanel.add( button ) ;
        }
        
        buttonPanel.add( new JLabel( "Choose Synapse" ) ) ;
        for ( i = 0 ; i < synapseTypes.length ; i ++ ) {
            button = new JRadioButton( synapseTypes[i].getName( ) ) ;
            if ( i == 0 ) {
                button.setSelected( true ) ;
                synapse = synapseTypes [ i ] ;
            }
            button.setActionCommand( "s" + i ) ;
            button.addActionListener( this ) ;
            synapses.add( button ) ;
            buttonPanel.add( button ) ;
        }
        
        buttonPanel.add( new JLabel( "Choose Current" ) ) ;
        for ( i = 0 ; i < currentTypes.length ; i ++ ) {
            button = new JRadioButton( currentTypes[i].getName( ) ) ;
            if ( i == 0 ) {
                button.setSelected( true ) ;
                IApp = currentTypes [ i ] ;
            }
            button.setActionCommand( "c" + i ) ;
            button.addActionListener( this ) ;
            currents.add( button ) ;
            buttonPanel.add( button ) ;
        }
        
        buttonPanel.add( new JLabel( "Choose Experiment" ) ) ;
        for ( i = 0 ; i < experimentTypes.length ; i ++ ) {
            button = new JRadioButton( experimentTypes[i].getName( ) ) ;
            if ( i == 0 ) {
                button.setSelected( true ) ;
                e = experimentTypes [ i ] ;
            }
            button.setActionCommand( "e" + i ) ;
            button.addActionListener( this ) ;
            experiments.add( button ) ;
            buttonPanel.add( button ) ;
        }
        
        goButton = new JButton( "Run Simulation" ) ;
        goButton.setActionCommand( "go" ) ;
        goButton.addActionListener( this ) ;
        goButton.setEnabled( true ) ;
        buttonPanel.add( goButton ) ;
        
        add( buttonPanel , BorderLayout.LINE_START ) ;
        
        updateOptionsPanel( ) ;
    }
    
    // uses the event thread to enable the go button
    public void enableGo( ) {
        javax.swing.SwingUtilities.invokeLater( new Runnable( ) {
            public void run( ) {
                goButton.setEnabled( true ) ;
            }
        } ) ;
    }
    
    
    private void updateOptionsPanel( ) {
        JScrollPane optionsPane = new JScrollPane( ) ;
        JPanel options = new JPanel( ) ;
        try {
            remove( optionsPanel ) ;
            nParams.commit( ) ;
            sParams.commit( ) ;
            cParams.commit( ) ;
            eParams.commit( ) ;
        } catch ( Exception ex ) {
        }
        try {
            optionsPane.setPreferredSize( new Dimension( getWidth( ) - buttonPanel.getWidth( ) , getHeight( ) - 10 ) ) ;
        } catch ( Exception ex2 ) {
        }
        nParams = new ParameterWindow( neuron ) ;
        sParams = new ParameterWindow( synapse ) ;
        cParams = new ParameterWindow( IApp ) ;
        eParams = new ParameterWindow( e ) ;
        optionsPanel = new JPanel( ) ;
        options.setLayout( new BoxLayout( options , BoxLayout.Y_AXIS ) ) ;
        options.add( new JLabel( "Neuron" ) ) ;
        options.add( nParams.getPanel( ) ) ;
        options.add( new JLabel( "Synapse" ) ) ;
        options.add( sParams.getPanel( ) ) ;
        options.add( new JLabel( "Current" ) ) ;
        options.add( cParams.getPanel( ) ) ;
        options.add( new JLabel( "Experiment" ) ) ;
        options.add( eParams.getPanel( ) ) ;
        optionsPane.getViewport().add( options ) ;
        optionsPanel.add( optionsPane ) ;
        add( optionsPanel , BorderLayout.CENTER ) ;
        
        // redisplay
        validate( ) ;
    }
    
    // paint is called on window resize, so we updateOptionsPanel here
    // to adjust the scrollpane size
    public void paint( Graphics g ) {
        super.paint( g ) ;
        updateOptionsPanel( ) ;
    }
    
    private void go( ) {
        final Experimenter experimenter = this ;
        
        goButton.setEnabled( false ) ;
        nParams.commit( ) ;
        sParams.commit( ) ;
        cParams.commit( ) ;
        eParams.commit( ) ;
        
        // do the simulation in a thread to keep gui responsive
        ( new Thread( new Runnable( ) {
            public void run( ) {
                ExperimentAnalyzer a ;
                INeuralNetExperiment f ;
                
                // clone the experiment, current, neuron and synapse to allow changing
                // of parameters immediately
                f = e.clone( ) ;
                f.init( neuron.clone( ) , synapse.clone( null , null , 0 ) ) ;
                f.setCurrent( IApp.clone( ) ) ;
                f.run( ) ;
//				new ExperimenterResults ( f , "Neural Net Experimenter:  " + count ) ;
                count ++ ;
                
                a = new ExperimentAnalyzer( f ) ;
                a.setExperimenter( experimenter ) ;
                a.run( ) ;
                a = null ;
                f = null ;
            }
        } ) ) . start( ) ;
    }
    
    public void actionPerformed( ActionEvent event ) {
        String command = event.getActionCommand( ) ;
        char c ;
        int n ;
        
        try {
            if ( command.equals( "go" ) ) {
                go( ) ;
                return ;
            }
            c = command.charAt( 0 ) ;
            n = Integer.parseInt( command.substring( 1 ) ) ;
            switch ( c ) {
                case 'c' :
                    cParams.commit( ) ;
                    IApp = currentTypes [ n ] ;
                    break ;
                case 'e' :
                    eParams.commit( ) ;
                    e = experimentTypes [ n ] ;
                    break ;
                case 'n' :
                    nParams.commit( ) ;
                    neuron = neuronTypes [ n ] ;
                    break ;
                case 's' :
                    sParams.commit( ) ;
                    synapse = synapseTypes [ n ] ;
                    break ;
            }
            updateOptionsPanel( ) ;
        } catch ( Exception ex ) {
        }
    }
    
    private static void newWindow( ) {
        Experimenter e = new Experimenter( ) ;
        JFrame window = new JFrame( "Experimenter" ) ;
        window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ) ;
        
        window.getContentPane().add( e ) ;
        
        window.setSize( 640 , 480 ) ;
        window.setVisible( true ) ;
        e.updateOptionsPanel( ) ;
    }
    
    public static void main( String [ ] args ) {
        javax.swing.SwingUtilities.invokeLater( new Runnable( ) {
            public void run( ) {
                newWindow( ) ;
            }
        } ) ;
    }
}
